#include "Demo.h"
#include "Script.h"
#include "Message.h"
#include <iostream>

bool g_Intermission = false;


Demo::Demo()
{
	m_LastBlock = 0;
	m_FirstBlock = 0;
}

Demo::~Demo()
{

}



bool Demo::Load(std::string Filename)
{
	FILE* file = fopen(Filename.c_str(),"rb");

	if (!file)
		return false;
	
	// skip the beginning
	char C = ' ';
	while (C != '\n')
	{
		fread(&C,1,1,file);
	}

	int Counter = 0;

	m_FirstBlock = 3;
	while (true)
	{
		try
		{
			bool HadLast = false;
			Block B (file);
			m_RawBlocks.push_back(B);

			if (B.IsLast())
			{
				m_LastBlock = Counter-1;
			}
			Counter++;
		}
		catch (const char* pMsg)
		{
			if (g_Verbal)
				std::cout << pMsg << std::endl;
			break;
		}
		catch (...)
		{
			if (g_Verbal)
				std::cout << "Cought an unexpected exception" << std::endl;
			break;
		}
	}

	if (g_Verbal)
		std::cout << "Loaded total of " << m_RawBlocks.size() << " blocks." << std::endl;
	
	m_CurrBlock = 0;
	while (m_RawBlocks[m_CurrBlock].GetUsedSize() < 5)
		m_CurrBlock++;

	return (m_RawBlocks.size() > 3);
}

bool Demo::Save(std::string Filename)
{
	FILE* file = fopen(Filename.c_str(),"wb");

	if (!file)
		return false;
	
	// beginning
	fwrite ("-1\n",3,1,file);

	for (int I = 0; I < m_RawBlocks.size(); I++)
	{
		if (!m_RawBlocks[I].Write(file))
		{
			fclose(file);
			return false;
		}
	}

	return true;
}

Block& Demo::GetCurrentBlock()
{	
	return m_RawBlocks[m_CurrBlock];
}


void Demo::MarkFirstBlock()
{
	m_FirstBlock = m_CurrBlock;
}
void Demo::MarkLastBlock()
{
	m_LastBlock = m_CurrBlock;
}



void Demo::Progress(int& Speed,float& Time,bool NextTime)
{
	switch (Speed)
	{
	case 0:
		break;
	case 1:
		Forward(Speed,NextTime);
		Speed = 0;
		break;
	case -1:
		Backward(Speed,NextTime);
		Speed = 0;
		break;
	case 2:
		Forward(Speed,NextTime);
		break;
	case -2:
		Backward(Speed,NextTime);
		break;
	case 3:
		Forward(Speed,NextTime);
		Forward(Speed,NextTime);
		Forward(Speed,NextTime);
		break;
	case -3:
		Backward(Speed,NextTime);
		Backward(Speed,NextTime);
		Backward(Speed,NextTime);
		break;
	}

	if (Time < m_RawBlocks[m_CurrBlock].GetBlockTime() ||
		Time > m_RawBlocks[m_CurrBlock+1].GetBlockTime())
		Time = m_RawBlocks[m_CurrBlock].GetBlockTime();
}


void Demo::Forward(int& Speed,bool NextTime)
{
	if (m_CurrBlock == m_LastBlock)
		Speed = 0;
	else
	{
		float T = m_RawBlocks[m_CurrBlock].GetBlockTime();
		m_CurrBlock++;
		while (NextTime &&
			m_CurrBlock < m_LastBlock && 
			(m_RawBlocks[m_CurrBlock].GetBlockTime() == 0 ||
			 m_RawBlocks[m_CurrBlock].GetBlockTime() == T ))
			 m_CurrBlock++;
	}
}


void Demo::Backward(int& Speed,bool NextTime)
{
	if (m_CurrBlock == m_FirstBlock)
		Speed = 0;
	else
	{
		float T = m_RawBlocks[m_CurrBlock].GetBlockTime();
		m_CurrBlock--;
		while (NextTime && 
			m_CurrBlock > m_FirstBlock && 
			(m_RawBlocks[m_CurrBlock].GetBlockTime() == 0 ||
			 m_RawBlocks[m_CurrBlock].GetBlockTime() == T ))
			 m_CurrBlock--;
	}
}

void Demo::SnapToTime(float Time)
{
	for (int I = 4; I < m_RawBlocks.size()-1;I++)
	{
		if (m_RawBlocks[I].GetBlockTime() <= Time && m_RawBlocks[I+1].GetBlockTime() >= Time)
		{
			m_CurrBlock = I;
			return;
		}
	}
}


float Demo::GetFirstTime()
{
	return m_RawBlocks[m_FirstBlock].GetBlockTime();
}

float Demo::GetLastTime()
{
	return m_RawBlocks[m_LastBlock].GetBlockTime();
}


bool Demo::Recam(Script& S,Demo& Out)
{
	float Pos[3];
	float Angs[3] = {0,0,0};
	float Time;

	Message::UsingRealTime = true;

	for (int I = 0; I < m_RawBlocks.size(); I++)
	{

		Out.m_RawBlocks.push_back(m_RawBlocks[I]);
		if (I >= 3 && !g_Intermission)
		{
			if (m_RawBlocks[I].IsLast())
			{
				g_Intermission = true;
			}
			else
			{
				Time = m_RawBlocks[I].GetBlockTime();
				S.UpdateCamera(Time,Pos,Angs);
				MessageUpdateEntity* pUpd = new MessageUpdateEntity(449,Pos,Angs);
				Out.m_RawBlocks[I].AddMessage(pUpd);
				Out.m_RawBlocks[I].SetCameraAngs(Angs);
			}

		}
	}
	return true;
}


void Demo::RenumberEntity(int OldId, int NewId)
{
	for (int I = 0; I < m_RawBlocks.size(); I++)
	{
		m_RawBlocks[I].RenumberEntity(OldId,NewId);
	}
}

void Demo::ShiftEntity(int Id, int Offset[3])
{
	for (int I = 0; I < m_RawBlocks.size(); I++)
	{
		m_RawBlocks[I].ShiftEntity(Id,Offset);
	}
}

void Demo::MergeInto(Demo* pOther,int ClientNumber)
{
	Message::UsingRealTime = true;

	int MyBlock = 0; 
	int OtherBlock = 0;

	int BlockCount = 0;

	g_Intermission = false;
	while (MyBlock < m_RawBlocks.size() && OtherBlock < pOther->m_RawBlocks.size())
	{
		if (m_RawBlocks[MyBlock].IsLast())
			g_Intermission = true;

		if (BlockCount++ >= 5)
		{
			while (MyBlock < m_RawBlocks.size() &&
					m_RawBlocks[MyBlock].GetBlockTime() == 0)
				MyBlock++;
			while (OtherBlock < pOther->m_RawBlocks.size() &&
				   pOther->m_RawBlocks[OtherBlock].GetBlockTime() == 0)
				OtherBlock++;

			if (MyBlock >= m_RawBlocks.size())
				break;
			if (OtherBlock >= pOther->m_RawBlocks.size())
				break;

			float MyTime = m_RawBlocks[MyBlock].GetBlockTime();
			float OtherTime = pOther->m_RawBlocks[OtherBlock].GetBlockTime();
			if (MyTime < OtherTime)
				MyBlock++;
			else if	(MyTime > OtherTime)
			{
				m_RawBlocks[MyBlock].MergeInto(pOther->m_RawBlocks[OtherBlock],ClientNumber);
				OtherBlock++;
			}
			else
			{
				m_RawBlocks[MyBlock].MergeInto(pOther->m_RawBlocks[OtherBlock],ClientNumber);
				MyBlock++;
				OtherBlock++;	
			}		
		}
		else
		{
			m_RawBlocks[MyBlock].MergeInto(pOther->m_RawBlocks[OtherBlock],ClientNumber);
			MyBlock++;
			OtherBlock++;	
		}
	
	}
}

void Demo::ShiftStats(int ClientNumber)
{
	for (int I = 0; I < m_RawBlocks.size(); I++)
	{
		if (m_RawBlocks[I].GetClientData())
			m_RawBlocks[I].GetClientData()->MergeInto(m_RawBlocks[I],ClientNumber);

	}
}
